package com.wangzhenyu.common.netcore.networking.http.core

import android.util.Log
import com.wangzhenyu.common.netcore.networking.http.exception.DataErrorException
import com.wangzhenyu.common.netcore.networking.http.exception.HttpErrorCodeException
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.suspendCancellableCoroutine
import retrofit2.Call
import retrofit2.Callback
import retrofit2.HttpException
import retrofit2.Invocation
import retrofit2.Response
import retrofit2.Retrofit
import java.io.IOException
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException

/**
 * @Desc 扩展框架协程，捕获异常统一处理。
 */

inline fun <reified T> Retrofit.create(): T = create(T::class.java)


/**
 * 等待请求返回。
 *
 */
suspend fun <T : Any?> Call<T>.awaitHiResponse(): HiResponse<T> {
    return awaitHiResponseInner(false)
}

@Throws(CancellationException::class, Exception::class)
suspend fun <T : Any?> Call<T>.awaitHiResponseWithException(): HiResponse<T> {
    return awaitHiResponseInner(true)
}

//函数会挂起当前协程，并给出一个 continuation 对象；直到调用 continuation.resume 恢复协程，或者抛出异常。
@Throws(CancellationException::class, Exception::class)
private suspend fun <T : Any?> Call<T>.awaitHiResponseInner(throwsException: Boolean): HiResponse<T> {

    try {
        val resp: HiResponse<T> = suspendCancellableCoroutine { continuation ->

            continuation.invokeOnCancellation {
                try {
                    cancel()
                } catch (ex: Throwable) {
                    //
                }
            }

            enqueue(object : Callback<T> {
                override fun onResponse(call: Call<T>, response: Response<T>) {

                    if (response.isSuccessful) {

                        val body: T? = response.body()
                        if (body == null) {
                            val invocation = call.request().tag(Invocation::class.java)!!
                            val method = invocation.method()
                            val e = IOException(
                                "Response from "
                                        + method.declaringClass.name + '.' + method.name
                                        + " was null but response body type was declared as non-null"
                            )
                            if (!call.isCanceled) {
                                continuation.resumeWithException(e)
                            }
                        } else {
                            if (!call.isCanceled) {
                                when (body) {
                                    is HiResponse<*> -> {
                                        continuation.resume(body as HiResponse<T>)
                                    }

                                    is Unit -> {
                                        continuation.resume(HiResponse<T>(0, "", body))
                                    }

                                    else -> {
                                        continuation.resume(HiResponse<T>(0, "", body))
                                    }
                                }

                            }
                        }


                    } else {
                        if (!call.isCanceled) {
                            continuation.resumeWithException(HttpException(response))
                        }
                    }
                }

                override fun onFailure(call: Call<T>, t: Throwable) {
                    if (!call.isCanceled) {
                        continuation.resumeWithException(t)
                    }
                }
            })

        }
        return resp
    } catch (ce: CancellationException) {
        ce.printStackTrace()
        throw ce
    } catch (e: Exception) {
        e.printStackTrace()
        Log.d("KotlinExtensions.kt", e.message.toString())

        val resp = HiResponse<T>()
        resp.msg = ""
        resp.isException = true
        when (e) {
            is DataErrorException -> {
                resp.code = HiResponseCode.DATA_ERROR
            }

            is HttpException -> {
                resp.code = e.code()
                val msg = e.response()?.errorBody()?.string()
                resp.msg = msg
            }

            is HttpErrorCodeException -> {
                resp.code = HiResponseCode.NET_FAILED
            }

            is IOException -> {
                resp.code = HiResponseCode.NET_FAILED
            }

            else -> {
                resp.code = HiResponseCode.UNKNOWN_FAILED
            }
        }

        if (throwsException) {
            throw e
        }
        return resp
    }


}
